package com.ywu.admin.user.standard.service;

import com.ywu.admin.user.standard.config.AppConfig;
import com.ywu.admin.user.standard.constants.UserInfoConstants;
import com.ywu.admin.user.standard.data.UserRepository;
import com.ywu.admin.user.standard.dto.UserDto;
import com.ywu.admin.user.standard.entity.UserEntity;
import com.ywu.admin.user.standard.utils.JwtUtils;
import com.ywu.app.manage.spi.dto.YwuAppUserDto;
import com.ywu.app.manage.spi.spi.YwuAppSpi;
import com.ywu.boot.config.BootConfigAppConfig;
import com.ywu.boot.context.*;
import com.ywu.boot.exception.YwuRuntimeException;
import com.ywu.boot.utils.RedisUtils;
import com.ywu.common.constant.CommonConstant;
import com.ywu.common.entity.PageList;
import com.ywu.common.gson.GsonUtils;
import com.ywu.common.page.PageService;
import com.ywu.common.result.CommonResult;
import com.ywu.common.utils.AesUtils;
import com.ywu.role.spi.dto.UserRoleAuthDto;
import com.ywu.role.spi.spi.UserRoleAuthSpi;
import com.ywu.tenant.spi.dto.TenantUserDTO;
import com.ywu.tenant.spi.entity.TenantEntity;
import com.ywu.tenant.spi.spi.TenantSpi;
import com.ywu.verify.spi.client.VerifyCodeSpi;
import com.ywu.verify.spi.dto.VerifyDto;
import com.ywu.verify.spi.dto.VerifyParamDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName UserService
 * @Description TODO
 * @Author GroundDemo
 * @Date 2024/3/9 13:45
 * @Version 1.0
 **/
@Service
@Slf4j
public class UserService {
    private static final String LOGIN_CHECK_REDIS_KEY = "YWU-ADMIN-USER:LOGIN-CHECK:";

    private static final String USER_TOKEN_REDIS_KEY = "YWU-ADMIN-USER:USER-TOKEN:";

    private static final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(10);

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private BootConfigAppConfig bootConfigAppConfig;

    @Autowired
    private VerifyCodeSpi verifyCodeSpi;

    @Autowired
    private TenantSpi tenantSpi;

    @Autowired
    private UserRoleAuthSpi userRoleAuthSpi;

    @Autowired
    private YwuAppSpi ywuAppSpi;

    /**
    * @Author GroundDemo
    * @Description 创建用户信息
    * @Date 13:46 2024/3/9
    * @Param [userDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> createUserInfo(UserDto userDto) {
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(userDto, userEntity);
        // 密码加密
        String encodePassword = bCryptPasswordEncoder.encode(userDto.getUserPassword());
        userEntity.setUserPassword(encodePassword);
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        userEntity.createInfo(currentContext.getYwuContextUser().getUserAccount());
        userRepository.createUserInfo(userEntity);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 修改用户信息
    * @Date 14:03 2024/3/9
    * @Param [userDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> updateUserInfo(UserDto userDto) {
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(userDto, userEntity);
        userEntity.setUserPassword(null);
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        userEntity.updateInfo(currentContext.getYwuContextUser().getUserAccount());
        userRepository.updateUserInfo(userEntity);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 删除用户信息
    * @Date 14:10 2024/3/9
    * @Param [userDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> deleteUserInfo(UserDto userDto) {
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(userDto, userEntity);
        userRepository.deleteUserInfo(userEntity);
        // 删除用户授权、租户授权、应用授权
        List<UserRoleAuthDto> userRoleAuthDtos = new ArrayList<>();
        UserRoleAuthDto userRoleAuthDto = new UserRoleAuthDto();
        userRoleAuthDto.setUserId(userEntity.getId());
        userRoleAuthDtos.add(userRoleAuthDto);
        userRoleAuthSpi.userRoleAuthDeleteInfo(userRoleAuthDtos);
        TenantUserDTO tenantUserDTO = new TenantUserDTO();
        tenantUserDTO.setUserId(userEntity.getId());
        tenantSpi.deleteTenantPermission(tenantUserDTO);
        YwuAppUserDto ywuAppUserDto = new YwuAppUserDto();
        ywuAppUserDto.setUserId(userEntity.getId());
        ywuAppSpi.appUserDeleteInfoByUserId(ywuAppUserDto);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 查询用户信息列表
    * @Date 14:13 2024/3/9
    * @Param [userDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> queryUserInfo(UserDto userDto) {
        // 查询数量
        int count = userRepository.queryUserCount(userDto);
        PageService.execPageStartAndEnd(userDto.getPageInfo());
        List<UserEntity> entityList = userRepository.queryUserInfo(userDto);
        PageList<UserEntity> userEntityPageList = new PageList<>(userDto.getPageInfo().getPageIndex(),
                userDto.getPageInfo().getPageSize(), count, entityList);
        return CommonResult.success(userEntityPageList);
    }

    /**
    * @Author GroundDemo
    * @Description 根据用户账号查询用户信息
    * @Date 16:46 2024/3/10
    * @Param [userDto]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<UserEntity> queryUserByUserAccount(UserDto userDto) {
        UserEntity entityList = userRepository.queryUserEntityByAccount(userDto);
        return CommonResult.success(entityList);
    }

    /**
    * @Author GroundDemo
    * @Description 登录
    * @Date 17:19 2024/3/10
    * @Param [userDto,response]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> loginUser(UserDto userDto, HttpServletResponse response) {
        if (StringUtils.isAnyBlank(userDto.getLoginCheckRandomStr(), userDto.getUserAccount())) {
            log.error("loginUser param is error");
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        // 校验redis中key是否存在
        String redisKey = LOGIN_CHECK_REDIS_KEY + userDto.getUserAccount();
        String redisValue = RedisUtils.getByKey(redisKey);
        if (Objects.isNull(redisValue) || !StringUtils.equals(redisValue, userDto.getLoginCheckRandomStr())) {
            log.error("loginUser redis value is not exist or error");
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        // 校验当前登录的租户与用户是否授权一致
        UserDto loginDto = new UserDto();
        loginDto.setUserAccount(userDto.getUserAccount());
        UserEntity userEntity = userRepository.queryUserEntityByAccount(loginDto);
        if (Objects.isNull(userEntity) || Objects.isNull(userEntity.getId())) {
            log.error("loginUser error, user not exist");
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        TenantUserDTO tenantUserDTO = new TenantUserDTO();
        tenantUserDTO.setUserId(userEntity.getId());
        CommonResult<List<TenantEntity>> queryRes = tenantSpi.queryTenantInfoByUserId(tenantUserDTO);
        List<TenantEntity> data = queryRes.getData();
        if (CollectionUtils.isEmpty(data)) {
            log.error("loginUser error, tenant ID is not exist");
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        List<TenantEntity> filterTenant = data.stream().filter(res -> StringUtils.equals(res.getTenantId(), userDto.getTenantId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(filterTenant)) {
            log.error("loginUser error, user tenant permission is not exist");
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        // 生成TOKEN信息
        userEntity.setUserAccount(userEntity.getUserAccount());
        buildTokenInfo(userEntity, userDto.getTenantId(), response);
        // 删除临时校验的redis值
        RedisUtils.deleteByKey(redisKey);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 构建Token信息并设置Cookie
    * @Date 21:40 2024/3/16
    * @Param []
    * @return void
    **/
    private void buildTokenInfo(UserEntity userEntity, String tenantId, HttpServletResponse response) {
        String decryptTokenKey;
        try {
            decryptTokenKey = AesUtils.decrypt(bootConfigAppConfig.getTokenKey(), bootConfigAppConfig.getSecretKey());
        } catch (Exception e) {
            log.error("loginUser error {}", e.getMessage());
            throw new YwuRuntimeException(e.getMessage());
        }
        String accessTokenInfo = JwtUtils.createToken(userEntity.getUserAccount(), tenantId, decryptTokenKey, appConfig.getAccessExpire());
        String refreshToken = JwtUtils.createToken(userEntity.getUserAccount(), tenantId, decryptTokenKey, appConfig.getRefreshExpire());
        Cookie accessTokenCookie = new Cookie("AccessToken", accessTokenInfo);
        Cookie refreshTokenCookie = new Cookie("RefreshToken", refreshToken);
        // 将生成的token存入redis中
        String redisKey = USER_TOKEN_REDIS_KEY + userEntity.getUserAccount();
        RedisUtils.set(redisKey, refreshToken);
        RedisUtils.setExpire(redisKey, appConfig.getRefreshExpire(), TimeUnit.MILLISECONDS);
        accessTokenCookie.setPath("/");
        refreshTokenCookie.setPath("/");
        response.addCookie(accessTokenCookie);
        response.addCookie(refreshTokenCookie);
    }

    /**
     * @Author GroundDemo
     * @Description 生成验证码
     * @Date 21:10 2024/2/23
     * @Param [response]
     * @return com.ywu.blog.common.result.CommonResult<java.lang.Object>
     **/
    public CommonResult<Object> loginVerifyCode(HttpServletResponse response) {
        // 调用验证码生成服务
        VerifyParamDto verifyParamDto = new VerifyParamDto();
        log.info("loginVerifyCode verify config: {}", appConfig.getVerify());
        verifyParamDto.setHeight(appConfig.getVerify().getHeight());
        verifyParamDto.setWidth(appConfig.getVerify().getWidth());
        verifyParamDto.setFontSize(appConfig.getVerify().getFontSize());
        verifyParamDto.setTypes(appConfig.getVerify().getTypes());
        CommonResult<Object> objectCommonResult = verifyCodeSpi.loginVerifyCode(verifyParamDto);
        if (!StringUtils.equals("200", objectCommonResult.getCode())) {
            return objectCommonResult;
        }
        VerifyDto verifyDto = GsonUtils.deserialize(GsonUtils.serialize(objectCommonResult.getData()), VerifyDto.class);
        // 生成UUID设置为Cookie
        Cookie verifyKey = new Cookie("verifyKey", verifyDto.getVerifyKey());
        response.addCookie(verifyKey);
        return CommonResult.success(verifyDto.getVerifyImageStr());
    }

    /**
    * @Author GroundDemo
    * @Description 根据ID批量查询用户信息
    * @Date 19:10 2024/5/19
    * @Param [ids]
    * @return com.ywu.common.result.CommonResult<java.util.List<com.ywu.admin.user.standard.entity.UserEntity>>
    **/
    public CommonResult<List<UserEntity>> queryUserByUserIdBatch(List<Integer> ids) {
        List<UserEntity> userEntities = userRepository.queryUserByIdBatch(ids);
        return CommonResult.success(userEntities);
    }

    /**
    * @Author GroundDemo
    * @Description 登录动作分为两步，第一步先校验用户名和密码，调用成功返回当前用户下的所有租户
    * @Date 21:09 2024/5/30
    * @Param [userDto, verifyKey, response]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> loginUserBeforeCheck(UserDto userDto, String verifyKey, HttpServletResponse response) {
        // 校验验证码
        CommonResult<Object> objectCommonResult = verifyCodeSpi.verifyLoginCode(new VerifyDto().setVerifyCode(userDto.getVerifyCode()).setVerifyKey(verifyKey));
        // 判断是否校验失败
        if (!StringUtils.equals("200", objectCommonResult.getCode())) {
            return objectCommonResult;
        }
        // 根据账号查询用户
        UserDto loginDto = new UserDto();
        loginDto.setUserAccount(userDto.getUserAccount());
        UserEntity userEntity = userRepository.queryUserEntityByAccount(loginDto);
        if (Objects.isNull(userEntity) || Objects.isNull(userEntity.getId())) {
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        // 校验密码
        if (!bCryptPasswordEncoder.matches(userDto.getUserPassword(), userEntity.getUserPassword())) {
            return CommonResult.error().setCode(CommonConstant.LOGIN_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_ERROR_MESSAGE);
        }
        // 生成随机字符串
        String loginCheckRandomStr = UUID.randomUUID().toString();
        String redisKey = LOGIN_CHECK_REDIS_KEY + userEntity.getUserAccount();
        RedisUtils.set(redisKey, loginCheckRandomStr);
        // 过期时间1小时
        RedisUtils.setExpire(redisKey, 1, TimeUnit.HOURS);
        // 查询当前用户下的所有租户信息
        TenantUserDTO tenantUserDTO = new TenantUserDTO();
        tenantUserDTO.setUserId(userEntity.getId());
        CommonResult<List<TenantEntity>> queryRes = tenantSpi.queryTenantInfoByUserId(tenantUserDTO);
        List<TenantEntity> data = queryRes.getData();
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("tenantData", data);
        resMap.put("loginCheckRandomStr", loginCheckRandomStr);
        // 将生成的随机字符串返回，后续登录租户时做入参传入
        return CommonResult.success(resMap);
    }

    /**
    * @Author GroundDemo
    * @Description 查询用户的详情，包括用户信息、角色信息、租户信息
    * @Date 14:40 2024/6/2
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> queryUserDetailInfo() {
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        // 获取上下文中的用户信息
        YwuContextUser ywuContextUser = currentContext.getYwuContextUser();
        // 获取上下文中的角色信息
        List<YwuContextRole> ywuContestRole = currentContext.getYwuContestRole();
        // 获取当前租户
        YwuContextTenant ywuContextTenant = currentContext.getYwuContextTenant();
        // 根据用户账号查询所有的租户信息
        TenantUserDTO tenantUserDTO = new TenantUserDTO();
        tenantUserDTO.setUserId(ywuContextUser.getId());
        CommonResult<List<TenantEntity>> queryRes = tenantSpi.queryTenantInfoByUserId(tenantUserDTO);
        List<TenantEntity> tenantEntityList = queryRes.getData();
        // 包装返回上下文
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("userInfo", ywuContextUser);
        resMap.put("roleInfo", ywuContestRole);
        resMap.put("tenantList", tenantEntityList);
        resMap.put("currentTenantInfo", ywuContextTenant);
        return CommonResult.success(resMap);
    }

    /**
    * @Author GroundDemo
    * @Description 用户登出
    * @Date 15:18 2024/6/2
    * @Param [userDto, response]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> loginOut(UserDto userDto, HttpServletResponse response) {
        // 判断当前上下文中的用户和传入的账号是否一致
        YwuContext currentContext = YwuContextHolder.getCurrentContext();
        String currentUserAccount = currentContext.getYwuContextUser().getUserAccount();
        if (!StringUtils.equals(currentUserAccount, userDto.getUserAccount())) {
            return CommonResult.error().setCode(CommonConstant.LOGIN_OUT_ERROR_CODE)
                    .setErrorCode(CommonConstant.LOGIN_OUT_ERROR_MESSAGE)
                    .setMessage(CommonConstant.LOGIN_OUT_ERROR_MESSAGE);
        }
        // 删除redis中的值
        String redisKey = USER_TOKEN_REDIS_KEY + userDto.getUserAccount();
        RedisUtils.deleteByKey(redisKey);
        // 删除cookie
        Cookie accessTokenCookie = new Cookie("AccessToken", "");
        Cookie refreshTokenCookie = new Cookie("RefreshToken", "");
        Cookie appInfoTokenCookie = new Cookie("currentAppInfoKey", "");
        accessTokenCookie.setMaxAge(0);
        refreshTokenCookie.setMaxAge(0);
        appInfoTokenCookie.setMaxAge(0);
        accessTokenCookie.setPath("/");
        refreshTokenCookie.setPath("/");
        appInfoTokenCookie.setPath("/");
        response.addCookie(accessTokenCookie);
        response.addCookie(refreshTokenCookie);
        response.addCookie(appInfoTokenCookie);
        return CommonResult.success(null);
    }

    /**
    * @Author GroundDemo
    * @Description 用户租户修改
    * @Date 16:56 2024/6/2
    * @Param [userDto, response]
    * @return com.ywu.common.result.CommonResult<java.lang.Object>
    **/
    public CommonResult<Object> userTenantChange(UserDto userDto, HttpServletResponse response) {
        // 判断入参的用户ID与租户ID的关系
        TenantUserDTO tenantUserDTO = new TenantUserDTO();
        tenantUserDTO.setUserId(userDto.getId());
        CommonResult<List<TenantEntity>> queryRes = tenantSpi.queryTenantInfoByUserId(tenantUserDTO);
        if (Objects.isNull(queryRes) || Objects.isNull(queryRes.getData())) {
            return CommonResult.error()
                    .setErrorCode(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE)
                    .setMessage(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE);
        }
        List<TenantEntity> tenantData = queryRes.getData();
        if (CollectionUtils.isEmpty(tenantData)) {
            return CommonResult.error()
                    .setErrorCode(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE)
                    .setMessage(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE);
        }
        List<TenantEntity> filterRes = tenantData.stream().filter(res -> StringUtils.equals(res.getTenantId(), userDto.getTenantId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(filterRes)) {
            return CommonResult.error()
                    .setErrorCode(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE)
                    .setMessage(UserInfoConstants.USER_TENANT_INFO_ERROR_CODE);
        }
        // 重新构建token
        UserEntity userEntity = new UserEntity();
        userEntity.setUserAccount(userDto.getUserAccount());
        buildTokenInfo(userEntity, userDto.getTenantId(), response);
        return CommonResult.success(null);
    }
}
